package cn.edu.seu.java.node;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;

public class JavaVersion {
	public String projectName;
	public String versionNum;
	private Map<String, ASTNode> uniqueNameToASTNode;
	private Map<IMethodBinding, Set<IMethodBinding>> calleeIMBindingToCallerIMBindings;

	public JavaVersion() {
		// unique name is one of two cases as follows: 
		// (1) ITypeBinding's key  or  (2) fullyQualifiedName derived from manual inspection 
		this.uniqueNameToASTNode = new HashMap<String, ASTNode>();
		this.calleeIMBindingToCallerIMBindings = new HashMap<IMethodBinding, Set<IMethodBinding>>();
	}

	public void addMethod(MethodDeclaration node) {
		IMethodBinding iMethodBinding = node.resolveBinding();
		if (iMethodBinding != null) {
			this.uniqueNameToASTNode.put(iMethodBinding.getKey(), node);
		} else {
			String qualifiedName = (String) node.getProperty("fullyQualifiedName");
			this.uniqueNameToASTNode.put(qualifiedName, node);
		}
	}
	
	public void addClass(TypeDeclaration node) {
		ITypeBinding iTypeBinding = node.resolveBinding();
		if (iTypeBinding != null) {
			this.uniqueNameToASTNode.put(iTypeBinding.getKey(), node);
		} else {
			String qualifiedName = (String) node.getProperty("fullyQualifiedName");
			this.uniqueNameToASTNode.put(qualifiedName, node);
		}
	}

	/**
	 * 
	 * @param uniqueName : uniqueName refers to binding key of method/class or
	 *  their fullyQualifiedName
	 * @return may be null if binding/fullyQualifiedName is not correctly available
	 * 
	 * recommand to use binding-key to search the desired method/class,
	 * if fails, try again using fullyQualifiedName of that method/class
	 * 
	 */
	public ASTNode searchMethodOrClassBy(String uniqueName) {
		return this.uniqueNameToASTNode.get(uniqueName);
	}

	public void addCallRelation(IMethodBinding callee, IMethodBinding caller) {
		if (callee != null && caller != null) {
			if (this.calleeIMBindingToCallerIMBindings.containsKey(callee)) {
				Set<IMethodBinding> value = this.calleeIMBindingToCallerIMBindings.get(callee);
				value.add(caller);
			} else {
				Set<IMethodBinding> set = new HashSet<IMethodBinding>();
				set.add(caller);
				this.calleeIMBindingToCallerIMBindings.put(callee, set);
			}
		}
	}

	public Set<IMethodBinding> getCallersOf(IMethodBinding calleeIMBinding) {
		Set<IMethodBinding> callerIMBinding = this.calleeIMBindingToCallerIMBindings.get(calleeIMBinding);
		if (callerIMBinding != null) {
			return callerIMBinding;
		} else {
			return Collections.emptySet();
		}
	}

}
